package nbcp.http.api;


import nbcp.base.comm.JsonMap;
import nbcp.base.comm.MyJsonUtil;
import nbcp.base.comm.StringMap;
import nbcp.base.extend.StringHelper;
import nbcp.http.HttpClient;
import nbcp.http.annotation.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static nbcp.base.utils.JavaKotlinUtil.listOf;

/**
 * Docker Api: 需要翻墙
 * https://docs.docker.com/engine/api/v1.42/#section/Authentication
 */
@HttpClient("docker-client")
public interface DockerClientApi {

    class DockerContainer {

        public static class HostConfigModel {
            public String NetworkMode;
        }

        public static class NetworkSettingModel {
            public String NetworkID;
            public String EndpointID;
            public String Gateway;
            public String IPAddress;
            public Integer IPPrefixLen;
            public String IPv6Gateway;
            public String MacAddress;
        }

        public static class NetworkSettingsModel {
            public Map<String, NetworkSettingModel> Networks;
        }

        public static class MountsModel {
            public String Type;
            public String Source;
            public String Destination;
            public String Mode;
            public Boolean RW;
            public String Propagation;
        }

        public String Id;
        public List<String> Names;
        public String Image;
        public String ImageID;
        public String Command;
        public Integer Created;
        public String State;
        public String Status;
        public HostConfigModel HostConfig;
        public NetworkSettingsModel NetworkSettings;
        public List<MountsModel> Mounts;


        public String getContainerName() {
            if (Names == null || Names.isEmpty()) {
                return "";
            }
            return Names.get(0).replace("/", "");
        }

        public String getContainerIp() {
            var values = NetworkSettings.Networks.values();
            if (values.isEmpty()) {
                return "";
            }
            return values.stream().findFirst().get().IPAddress;
        }
    }

    enum DockerContainerStatusEnum {
        exited, dead, paused
    }

    /**
     * 获取所有容器
     *
     * @return
     */
    default List<DockerContainer> containers() {
        return containers(null, null, null);
    }

    /**
     * 按名称模糊查询容器
     *
     * @param imageNameVersion 镜像名称及版本号，必须完整
     * @remark: map[string][]string 的含义是: map<String, String[]>,即值是string[]格式。
     * @see https://www.jianshu.com/p/42f92763ad41
     */
    default List<DockerContainer> containers(
            String containerName,
            String imageNameVersion,
            DockerContainerStatusEnum[] status
    ) {
        var map = new JsonMap();
        if (StringHelper.hasValue(containerName)) {
            map.put("name", listOf(containerName));
        }
        if (StringHelper.hasValue(imageNameVersion)) {
            map.put("ancestor", listOf(imageNameVersion));
        }
        if (status != null && status.length > 0) {
            map.put("status", status);
        }
        return ori_all_containers(null, MyJsonUtil.toJson(map));
    }


    @Get("/containers/json")
    List<DockerContainer> ori_all_containers(
            @RequestParam() Boolean all,
            @RequestParam() String filters
    );


    /**
     * 杀掉一个容器
     *
     * @param id
     */
    @Post("/containers/{id}/kill")
    @NonThrow
    void killContainer(@PathVariable String id);

    /**
     * 启动一个现有的容器
     *
     * @param id
     */
    @Post("/containers/{id}/start")
    @NonThrow
    void startContainer(@PathVariable String id);


    class ContainerJson {

        public static class StateModel {
            public String Status;
            public Boolean Running;
            public Boolean Paused;
            public Boolean Restarting;
            public Boolean OOMKilled;
            public Boolean Dead;
            public Integer Pid;
            public Integer ExitCode;
            public String Error;
            public String StartedAt;
            public String FinishedAt;
        }


        public static class LogConfigModel {
            public String Type;
            public Map Config;
        }


        public static class RestartPolicyModel {
            public String Name;
            public Integer MaximumRetryCount;
        }


        public static class HostConfigModel {
            public List<String> Binds;
            public String ContainerIDFile;
            public LogConfigModel LogConfig;
            public String NetworkMode;
            public Map PortBindings;
            public RestartPolicyModel RestartPolicy;
            public Boolean AutoRemove;
            public String VolumeDriver;
            public List<Integer> ConsoleSize;

            public String CgroupnsMode;
            public List<String> Dns;
            public List<String> DnsOptions;
            public List<String> DnsSearch;
            public List<String> ExtraHosts;
            public String IpcMode;
            public String Cgroup;
            public Integer OomScoreAdj;
            public String PidMode;
            public Boolean Privileged;
            public Boolean PublishAllPorts;
            public Boolean ReadonlyRootfs;
            public String UTSMode;
            public String UsernsMode;
            public Integer ShmSize;
            public String Runtime;
            public String Isolation;
            public Integer CpuShares;
            public Integer Memory;
            public Integer NanoCpus;
            public String CgroupParent;
            public Integer BlkioWeight;
            public List<String> BlkioWeightDevice;
            public List<String> BlkioDeviceReadBps;
            public List<String> BlkioDeviceWriteBps;
            public List<String> BlkioDeviceReadIOps;
            public List<String> BlkioDeviceWriteIOps;
            public Integer CpuPeriod;
            public Integer CpuQuota;
            public Integer CpuRealtimePeriod;
            public Integer CpuRealtimeRuntime;
            public String CpusetCpus;
            public String CpusetMems;
            public List<String> Devices;
            public Object DeviceCgroupRules;
            public Object DeviceRequests;
            public Integer MemoryReservation;
            public Integer MemorySwap;
            public Object MemorySwappiness;
            public Boolean OomKillDisable;
            public Integer CpuCount;
            public Integer CpuPercent;
            public Integer IOMaximumIOps;
            public Integer IOMaximumBandwidth;
            public List<String> MaskedPaths;
            public List<String> ReadonlyPaths;
        }


        public static class DataModel {
            public String LowerDir;
            public String MergedDir;
            public String UpperDir;
            public String WorkDir;
        }

        public static class GraphDriverModel {
            public DataModel Data;
            public String Name;
        }


        public static class MountsModel {
            public String Type;
            public String Source;
            public String Destination;
            public String Mode;
            public Boolean RW;
            public String Propagation;
        }


        public static class ConfigModel {
            public String Hostname;
            public String Domainname;
            public String User;
            public Boolean AttachStdin;
            public Boolean AttachStdout;
            public Boolean AttachStderr;
            public Boolean Tty;
            public Boolean OpenStdin;
            public Boolean StdinOnce;
            public List<String> Env;
            public List<String> Cmd;
            public String Image;
            public Object Volumes;
            public String WorkingDir;
            public Map Labels;
        }


        public static class NetworkModel {
            public Object IPAMConfig;
            public Object Links;
            public List<String> Aliases;
            public String NetworkID;
            public String EndpointID;
            public String Gateway;
            public String IPAddress;
            public Integer IPPrefixLen;
            public String IPv6Gateway;
            public String GlobalIPv6Address;
            public Integer GlobalIPv6PrefixLen;
            public String MacAddress;
            public Object DriverOpts;
        }


        public static class NetworkSettingsModel {
            public String Bridge;
            public String SandboxID;
            public Boolean HairpinMode;
            public String LinkLocalIPv6Address;
            public Integer LinkLocalIPv6PrefixLen;
            public Map Ports;
            public String SandboxKey;
            public Object SecondaryIPAddresses;
            public Object SecondaryIPv6Addresses;
            public String EndpointID;
            public String Gateway;
            public String GlobalIPv6Address;
            public Integer GlobalIPv6PrefixLen;
            public String IPAddress;
            public Integer IPPrefixLen;
            public String IPv6Gateway;
            public String MacAddress;
            public Map<String, NetworkModel> Networks;
        }

        public String Id;
        public String Created;
        public String Path;
        public List<String> Args;
        public StateModel State;
        public String Image;
        public String ResolvConfPath;
        public String HostnamePath;
        public String HostsPath;
        public String LogPath;
        public String Name;
        public Integer RestartCount;
        public String Driver;
        public String Platform;
        public String MountLabel;
        public String ProcessLabel;
        public String AppArmorProfile;
        public HostConfigModel HostConfig;
        public GraphDriverModel GraphDriver;
        public List<MountsModel> Mounts;
        public ConfigModel Config;
        public List<NetworkSettingsModel> NetworkSettings;
    }


    /**
     * 获取容器的详情
     *
     * @param id
     * @return
     */
    @Get("/containers/{id}/json")
    @NonThrow
    ContainerJson inspectContainer(@PathVariable String id);


    class ContainerAddModel {

        public static class HostConfigModel {
            public List<String> Binds = new ArrayList<>();
            public List<String> Links = new ArrayList<>();
            public Integer Memory = 0;

            public Integer NanoCpus = 0;

            public Integer CpuShares = 0;
            public Integer CpuPeriod = 0;

            public Map<String, Object> PortBindings = new LinkedHashMap<>();
            public Boolean PublishAllPorts = false;
            public Boolean Privileged = false;
            public Boolean ReadonlyRootfs = false;
            public List<String> Dns = new ArrayList<>();

            public String NetworkMode = "bridge";

            //  /dev/shm 目录大小
            public Integer ShmSize = 0;
        }

        public List<String> Env = new ArrayList<>();
        public List<String> Cmd = new ArrayList<>();
        public String Entrypoint = "";
        public String Image = "";
        public Map<String, Object> Labels = new LinkedHashMap<>();
        public Map<String, Object> Volumes = new LinkedHashMap<>();
        public String WorkingDir = "";
        public Map<String, Object> ExposedPorts = new LinkedHashMap<>();
        public HostConfigModel HostConfig = new HostConfigModel();
    }


    class IdModel {
        public String Id = "";
    }

    /**
     * 生成容器
     *
     * @param model
     * @return
     */
    @Post("/containers/create")
    IdModel addContainer(@RequestBody ContainerAddModel model);


    /**
     * 删除容器
     *
     * @param id
     * @param force
     */
    @Delete("/containers/{id}")
    @NonThrow
    void removeContainer(@PathVariable("id") String id, @RequestParam("force") Boolean force);


    class DockerImage {
        public String Id = "";
        public List<String> RepoTags = new ArrayList<>();
        public Integer Size = 0;
    }


    default List<DockerImage> images() {
        return images(null);
    }

    /**
     * @param imageName 镜像名称，后面可以根版本号，也可能省略版本号。
     */
    default List<DockerImage> images(
            @RequestParam() String imageName
    ) {
        var map = new JsonMap();

        if (StringHelper.hasValue(imageName)) {
            map.put("reference", listOf(imageName));
        }
        return ori_all_images(null, MyJsonUtil.toJson(map));
    }

    /**
     * 获取所有镜像
     *
     * @param all
     * @param filters
     * @return
     */
    @Get("/images/json")
    List<DockerImage> ori_all_images(
            @RequestParam() Boolean all,
            @RequestParam() String filters
    );


    public static class DockerImageDetail extends DockerImage {

        public static class DockerImageConfig {
            public List<String> Env = new ArrayList<>();
            public List<String> Cmd = new ArrayList<>();
            public String WorkingDir = "";
            public String Entrypoint = "";
        }


        public static class DockerImageRootFS {
            public String Type = "";
            public List<String> Layers = new ArrayList<>();
        }

        public String Parent = "";
        public String Comment = "";
        public String Author = "";
        public String DockerVersion = "";
        public String Created = "";
        public DockerImageConfig Config = new DockerImageConfig();
        public DockerImageRootFS RootFS = new DockerImageRootFS();
    }


    /**
     * 镜像详情
     */
    @Get("/images/{name}/json")
    @NonThrow
    DockerImageDetail imageInfo(
            @PathVariable("name") String name
    );


    /**
     * 删除镜像
     *
     * @param name
     * @return
     */
    @Delete("/images/{name}")
    @NonThrow
    List<StringMap> deleteImage(
            @PathVariable("name") String name
    );

    @Post
    @NonThrow
    void tag(@PathVariable("name") String name, String repo, String tag);

    /**
     * 推送镜像
     */
    @Post("/push")
    @NonThrow
    void push(String name, String tag);


    default void build(String tag, @RequestBody byte[] body) {
        ori_build(tag, "", "", "", false, "2", body);
    }

    /**
     * 构建镜像
     *
     * @param t
     */
    @Post("/build")
    @NonThrow
    void ori_build(String t, String extrahosts, String remote, String buildargs, Boolean squash, String version,
                   @RequestBody byte[] body);
}
